package com.yricky.memoria.data

import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.google.gson.annotations.Expose
import com.yricky.memoria.data.pojo.PdfQuery
import com.yricky.memoria.data.pojo.SectionQuery
import com.yricky.memoria.utils.*
import java.io.File
import java.util.*
import kotlin.collections.ArrayList

/**
 * @author Yricky
 * @date 2021/11/21
 */
class WorkSpaceModel(private val dir: File) {
    private val subjectModels:WeakHashMap<String/* uid */,SubjectModel> = WeakHashMap()

    fun addSubject(name:String):SubjectMetaData?{
        if(name.length > 40)
            return null

        val uid:String = UID.subject()
        val values = ContentValues()
        values.put(SUID, uid)
        values.put(SNAME, name)
        values.put(COLOR, 0)

        // MetaData写入数据库
        dbHelper.db.insert(
            SubjectMetaTable.NAME,
            null,
            values
        )

        return SubjectMetaData(
            uid = uid,
            name = name
        )
    }

    fun delSubject(metaData: SubjectMetaData):Boolean{
        if(File(dir,metaData.uid).deleteRecursively()){
            // 删除成功
        } else {
            // 删除失败
            return false
        }
        subjectModels.remove(metaData.uid)
        dbHelper.db.delete(
            SubjectMetaTable.NAME,
            "$SUID = ?",
            arrayOf(metaData.uid)
        )
        return true
    }

    fun modSubject(metaDataOld: SubjectMetaData,metaDataNew: SubjectMetaData):Boolean{
        if (metaDataOld.uid != metaDataNew.uid)
            return false

        dbHelper.db.execSQL(
            "insert or replace into ${SubjectMetaTable.NAME}($SUID,$SNAME,$COLOR) values(?,?,?)",
            arrayOf(metaDataOld.uid,metaDataNew.name,metaDataNew.color)
        )

        return true
    }

    fun getSubjects(name: String = ""):List<SubjectMetaData>{
        return dbHelper.db.query(
            SubjectMetaTable.NAME,
            arrayOf(SUID, SNAME, COLOR),
            "$SNAME like ?",
            arrayOf("$name%"),
            null,
            null,
            SNAME,
            null
        ).use{
            val list = ArrayList<SubjectMetaData>()

            val uidIndex = it.getColumnIndex(SUID)
            val nameIndex = it.getColumnIndex(SNAME)

            if(
                uidIndex >= 0 &&
                nameIndex >= 0 &&
                it.count > 0
            ){
                it.moveToFirst()
                (0 until it.count).forEach { _ ->
                    list.add(SubjectMetaData(
                        uid = it.getString(uidIndex),
                        name = it.getString(nameIndex),
                    ))
                    it.moveToNext()
                }
            }

            list
        }
    }

    fun getSubjectsByTag(tagName: String = ""):List<SubjectMetaData>{
        val subMetaDataList = ArrayList<SubjectMetaData>()
        dbHelper.db.transaction{
            val subList:List<String> = tagManager.getRelatedWhichs(tagName) // 获取该标签关联的所有Subject的UID
            subList.forEach { sUid ->    // 对每个Subject根据其UID值获取MetaData
                query(
                    SubjectMetaTable.NAME,
                    arrayOf(SUID, SNAME, COLOR),
                    "$SUID = ?",
                    arrayOf(sUid),
                    null,
                    null,
                    null,
                    null
                ).run {
                    val uidIndex = this.getColumnIndex(SUID)
                    val nameIndex = this.getColumnIndex(SNAME)
                    val colorIndex = this.getColumnIndex(COLOR)

                    if(
                        uidIndex >= 0 &&
                        nameIndex >= 0 &&
                        colorIndex >= 0 &&
                        this.count > 0
                    ){
                        this.moveToFirst()
                        subMetaDataList.add(
                            SubjectMetaData(
                                uid = this.getString(uidIndex),
                                name = this.getString(nameIndex),
                                color = this.getInt(colorIndex)
                            )
                        )
                    }

                    this.close()
                }

            }
        }
        return subMetaDataList
    }

    /**
     * 给一个历史的SubjectMetaData，获取现在该MetaData的状态。
     * 若返回的MetaDataUID为空，则该subject已经被删除。
     */
    fun querySubjectStat(metaData:SubjectMetaData?):SubjectMetaData{
        if(metaData == null)
            return SubjectMetaData("","")
        return dbHelper.db.query(
            SubjectMetaTable.NAME,
            arrayOf(SUID, SNAME, COLOR),
            "$SUID like ?",
            arrayOf(metaData.uid),
            null,
            null,
            SNAME,
            null
        ).use{
            val uidIndex = it.getColumnIndex(SUID)
            val nameIndex = it.getColumnIndex(SNAME)

            if(
                uidIndex >= 0 &&
                nameIndex >= 0 &&
                it.count == 1
            ){
                it.moveToFirst()
                SubjectMetaData(
                    uid = it.getString(uidIndex),
                    name = it.getString(nameIndex),
                )
            }else{
                SubjectMetaData("","")
            }
        }
    }

    fun openSubject(metaData:SubjectMetaData):SubjectModel?{
        return subjectModels[metaData.uid] ?: querySubjectStat(metaData).let{ mdNew ->
            if(mdNew.uid.isNotBlank())
            SubjectModel(
                this,
                File(dir,mdNew.uid),
                mdNew
            ).also {
                subjectModels[it.uid] = it
            }else null
        }
    }

    fun getSection(sectionQuery: SectionQuery):Section?{
        return sectionQuery.let {
            openSubject(it.subjectMetaData)?.openSection(it.sectionMetaData)
        }
    }
    fun getPdf(query: PdfQuery):PdfDoc?{
        return query.let {
            openSubject(query.subjectMetaData)?.openPdf(query.pdfName)
        }
    }

    val tagManager:TagManager by lazy{
        TagManager(globalContext,dir, SUID)
    }

    fun addTextItem(text:String){
        Executor.exec{
            jsonData.items = jsonData.items.toMutableList().apply{
                add(0,Section.EmptyTextItem.also {
                    it.content = text
                })
                if(size>500){
                    removeLast()
                }
            }
            File(dir,"data.json").writeText(gson.toJson(jsonData))
        }
    }

    fun getItems():List<Section.Item>{
        return jsonData.items
    }

    private val jsonData: Section.JsonData by lazy {
        try {
            gson.fromJson(File(dir,"data.json").readText(), Section.JsonData::class.java)
        }catch (e:Throwable){
            Section.JsonData().also {
                File(dir,"data.json").writeText(gson.toJson(it))
            }
        }
    }


    companion object{
        const val DB_VERSION = 1
        private const val SUID = "subUid"
        const val SNAME = "name"
        const val COLOR = "color"
        private object SubjectMetaTable{
            const val NAME = "subMetadata"
            fun initIn(db:SQLiteDatabase){
                db.execSQL("create table $NAME($SUID text primary key not null,$SNAME text not null,$COLOR integer not null)")
            }
        }
    }
    private val dbHelper = object : SQLiteOpenHelper(
        globalContext,
        File(dir,"metadata.db").path,
        null,
        DB_VERSION){
        val db by lazy { writableDatabase }

        override fun onCreate(db: SQLiteDatabase?) {
            db?.transaction {
                SubjectMetaTable.initIn(this)
            }
        }

        override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {

        }

    }

    class SubjectMetaData(
        @Expose val uid:String,
        @Expose val name:String,
        @Expose val color:Int = 0,
    ){
        override operator fun equals(other: Any?): Boolean {
            return other is SubjectMetaData &&
                    uid == other.uid &&
                    name == other.name &&
                    color == other.color
        }

        override fun hashCode(): Int {
            var result = uid.hashCode()
            result = 31 * result + name.hashCode()
            result = 31 * result + color
            return result
        }
    }
}